package com.smartfast4j.netty.echoserver;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

/**
 * 所有的Netty服务器都需要一个引导服务器
 * 绑定到服务器将在其上监听并接受传入连接请求的端口；
 * 配置Channel ，以将有关的入站消息通知给EchoServerHandler实例。
 */
public class EchoServer {
    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args)
        throws Exception {
        if (args.length != 1) {
            System.err.println("Usage: " + EchoServer.class.getSimpleName() +
                " <port>"
            );
            return;
        }
        int port = Integer.parseInt(args[0]);
        new EchoServer(port).start();//使用指定的端口启动服务器
    }

    public void start() throws Exception {
        //处理具体业务逻辑的ChannelHandler
        final EchoServerHandler serverHandler = new EchoServerHandler();
        //用于处理连接的生命周期中所发生的事件
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            /**
             * 创建一个ServerBootstrap 实例。使用的是NIO传输，所以指定了NioEventLoopGroup来接受和处理新的连接，
             * 并且将Channel的类型指定为NioServerSocketChannel。
             * 将本地地址设置为一个具有选定端口的InetSocketAddress，服务器将绑定到这个地址以监听新的连接请求。
             */
            //创建一个 ServerBootstrap 的实例以引导和绑定服务器
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                .channel(NioServerSocketChannel.class)//指定所使用的NIO传输Channel
                .localAddress(new InetSocketAddress(port))//使用指定的端口设置套接字地址
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    /**
                     * 当一个新的连接被接受时，一个新的子Channel将会被创建，
                     * 而ChannelInitializer将会把一个EchoServerHandler的实例添加到该Channel的ChannelPipeline中。
                     * ChannelHandler安装到ChannelPipeline 中的过程：
                         一个ChannelInitializer的实现被注册到了ServerBootstrap中
                         当ChannelInitializer.initChannel()方法被调用时，ChannelInitializer将在ChannelPipeline 中安装一组自定义的ChannelHandler；
                         ChannelInitializer 将它自己从ChannelPipeline 中移除。
                     */
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        /**
                         * ChannelPipeline 提供了 ChannelHandler 链的容器，并定义了用于在该链上传播入站
                         和出站事件流的 API。当 Channel 被创建时，它会被自动地分配到它专属的 ChannelPipeline。
                         */
                        ch.pipeline().addLast(serverHandler);
                    }
                });

            /**
             *
             * 异步地绑定服务器；调用sync()方法阻塞等待直到绑定完成(对sync()方法的调用将导致当前Thread阻塞，一直到绑定操作完成为止)
             */
            ChannelFuture f = b.bind().sync();
            System.out.println(EchoServer.class.getName() +
                " started and listening for connections on " + f.channel().localAddress());

            /**
             * 获取 Channel 的CloseFuture，并且阻塞当前线程直到它完成
             */
            f.channel().closeFuture().sync();
        } finally {
            //关闭 EventLoopGroup，释放所有的资源
            group.shutdownGracefully().sync();
        }
    }
}

/*
* 编写一个引导服务器的过程中所需要的步骤如下：
    创建一个 ServerBootstrap 的实例以引导和绑定服务器；
    创建并分配一个 NioEventLoopGroup 实例以进行事件的处理，如接受新连接以及读/写数据；
    指定服务器绑定的本地的 InetSocketAddress；
    使用一个 EchoServerHandler 的实例初始化每一个新的 Channel；
    调用 ServerBootstrap.bind()方法以绑定服务器。
* */
